/*
 * COX1205.c
 *
 *  Created on: 2012/10/22
 *      Author: shenming
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rule_info.h"
#include "core.h"
#include "trace.h"
#include "trace_valuecal.h"

static const PSZ RuleID = "COX1205";

#define NG_KIND_OVER	1
#define NG_KIND_FLOAT	2
#define NG_KIND_CALERR	3

static const PSZ MESSAGE_ENG = "";

static const PSZ MESSAGE_JPN_OVER = "代入によるオーバーフロー・アンダーフローの可能性があります";
static const PSZ MESSAGE_JPN_FLOAT = "浮動小数点型関連代入は処理できません";
static const PSZ MESSAGE_JPN_CALERR = "演算不可のため処理できません";

static const PSZ MESSAGE_CHS = "";

static const U2 RANK = MSG_RANK_ERR;

static PRULEOUT pRuleHead;
static PRULEOUT pRuleTail;

/******************************************************************
 * static function:
 * isValueScpInRange
 *******************************************************************/
static BOOL isValueScpInRange(TRVALUESCP * scp1, TRVALUESCP * scp2) {
	TRVALUE min1 = scp1->min;
	TRVALUE max1 = scp1->max;
	TRVALUE min2 = scp2->min;
	TRVALUE max2 = scp2->max;
	if (Tr_ValueLessOrEqual(&min2, &max2) && Tr_ValueLessOrEqual(&min1, &min2)
			&& Tr_ValueLessOrEqual(&max2, &max1)) {
		return TRUE;
	}
	return FALSE;
}

/******************************************************************
 * static function:
 * checkNGKind
 *******************************************************************/
static INT checkNGKind(AST cmdIndex) {
	INT ngKind = RULE_RESULT_OK;

	AST leftOpIdx = 0;
	AST leftOpCmd = 0;
	AST leftType = 0;

	AST rightOpIdx = 0;
	AST rightOpCmd = 0;
	AST rightType = 0;

	TRVALUESCP leftValueScp;
	TRVALUESCP rightValueScp;

	leftOpIdx = LinkCommandList.getLeftOperand(cmdIndex);
	if (LinkOperand.isCommandList(leftOpIdx)) {
		leftOpCmd = LinkOperand.getParameter(leftOpIdx);
		leftType = LinkCommandList.getType(leftOpCmd);
		if (!LinkTypeInfo.isBasicType(leftType)) {
			return ngKind;
		}
		if (LinkTypeInfo.isFloating(leftType)) {
			return ngKind = NG_KIND_FLOAT;
		}
		Tr_ScpGetScpByType(leftType, &leftValueScp);
		if (leftValueScp.calError) {
			return ngKind = NG_KIND_CALERR;
		}
	} else {
		return ngKind;
	}

	rightOpIdx = LinkCommandList.getRightOperand(cmdIndex);
	if (LinkOperand.isCommandList(rightOpIdx)) {
		rightOpCmd = LinkOperand.getParameter(rightOpIdx);
		rightType = LinkCommandList.getType(rightOpCmd);
		if (!LinkTypeInfo.isBasicType(rightType)) {
			return ngKind;
		}
		if (LinkTypeInfo.isFloating(rightType)) {
			return ngKind = NG_KIND_FLOAT;
		}
		Tr_ScpGetCmdValue(rightOpCmd, &rightValueScp);
		if (rightValueScp.calError) {
			return ngKind = NG_KIND_CALERR;
		}
	} else {
		return ngKind;
	}

	if (!isValueScpInRange(&leftValueScp, &rightValueScp)) {
		return ngKind = NG_KIND_OVER;
	}

	return ngKind;
}

/******************************************************************
 * main logic
 *******************************************************************/
static S4 RuleMain(AST cmdIndex, U4 event) {

	INT result = RULE_RESULT_OK;
	PRULEOUT newNode = NULL;
	PRULEOUT tmpNode = NULL;
	PRULEOUT tmpNode1 = NULL;

	if (!(event & EVENT_CMDLIST_PREV)) {
		return EXIT_SUCCESS;
	}

	if (!LinkCommandList.isAssignTo(cmdIndex)) {
		return EXIT_SUCCESS;
	}

	result = checkNGKind(cmdIndex);

	/*re-checked?*/
	tmpNode = pRuleHead;
	while (tmpNode != NULL ) {
		if (tmpNode->indexInfo == cmdIndex) {
			if (result > RULE_RESULT_OK) {
				result = RULE_RESULT_OK;
			} else {
				if (tmpNode == pRuleHead) {
					pRuleHead = pRuleHead->next;
					free(tmpNode);
				} else if (tmpNode == pRuleTail) {
					tmpNode1->next = NULL;
					pRuleTail = tmpNode1;
					free(tmpNode);
				} else {
					tmpNode1->next = tmpNode->next;
					free(tmpNode);
				}
			}
			break;
		}
		tmpNode1 = tmpNode;
		tmpNode = tmpNode->next;
	}

	/*make result struct*/
	if (result > RULE_RESULT_OK) {
		newNode = (PRULEOUT) malloc(sizeof(RULEOUT));
		if (pRuleHead == NULL ) {
			pRuleHead = newNode;
		}

		newNode->indexInfo = cmdIndex;
		newNode->fileIndex = CCodeScope.getFileIndex(
				LinkCommandList.getCodeScope(cmdIndex));
		newNode->beginLine = CCodeScope.getBeginLine(
				LinkCommandList.getCodeScope(cmdIndex));
		newNode->beginCol = CCodeScope.getBeginLineOffset(
				LinkCommandList.getCodeScope(cmdIndex));
		newNode->endLine = CCodeScope.getEndLine(
				LinkCommandList.getCodeScope(cmdIndex));
		newNode->endCol = CCodeScope.getEndLineOffset(
				LinkCommandList.getCodeScope(cmdIndex));
		newNode->ruleID = RuleID;
		newNode->errRank = RANK;

		sprintf(newNode->msgBuf_eng, MESSAGE_ENG);
		sprintf(newNode->msgBuf_chs, MESSAGE_CHS);

		switch (result) {
		case NG_KIND_OVER:
			sprintf(newNode->msgBuf_jpn, MESSAGE_JPN_OVER);
			break;
		case NG_KIND_FLOAT:
			sprintf(newNode->msgBuf_jpn, MESSAGE_JPN_FLOAT);
			break;
		case NG_KIND_CALERR:
			sprintf(newNode->msgBuf_jpn, MESSAGE_JPN_CALERR);
			break;
		default:
			break;
		}

		newNode->rfileIndex = 0;
		newNode->rlocLine = 0;
		newNode->next = NULL;

		if (pRuleTail != NULL ) {
			pRuleTail->next = newNode;
		}

		pRuleTail = newNode;
	}

	return EXIT_SUCCESS;
}

VOID COX1205_register(VOID) {
	PRULEMAN * pCurRule = &stRuleManager;
	PRULEMAN newRuleMan = (PRULEMAN) malloc(sizeof(RULEMAN));
	memset(newRuleMan, 0, sizeof(RULEMAN));
	newRuleMan->ruleID = RuleID;
	newRuleMan->ruleSwitch = OFF;
	newRuleMan->headOut = &pRuleHead;
	newRuleMan->tailOut = &pRuleTail;
	newRuleMan->ruleMain_cmd = RuleMain;
	newRuleMan->ruleMain_ast = NULL;
	newRuleMan->nextRule = NULL;
	if (stRuleManager == NULL ) {
		stRuleManager = newRuleMan;
	} else {
		while ((*pCurRule)->nextRule != NULL ) {
			pCurRule = &((*pCurRule)->nextRule);
		}
		(*pCurRule)->nextRule = newRuleMan;
	}
}

